package com.boco.blch.runner.executor;

import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;

import com.boco.blch.runner.command.MultipleCommand;
import com.boco.blch.runner.executor.AbstractExecutor.StdoutThread;
import com.boco.blch.runner.login.UserInfo;
import com.boco.blch.runner.result.MultipleResult;
import com.boco.blch.runner.result.Result;
import com.boco.blch.runner.result.ResultInfo;
import com.boco.blch.runner.result.handler.MultipleResultHandler;
import com.boco.blch.runner.result.handler.TrimResultForAIX;
import com.boco.blch.runner.util.ErrorMessageUtil;
import com.boco.blch.runner.util.ObjectUtil;
import com.trilead.ssh2.ChannelCondition;
import com.trilead.ssh2.Connection;
import com.trilead.ssh2.Session;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

/**
 * 
 * @author someone
 *
 */
public class MultipleCommandExecutor extends AbstractExecutor {
	private static Logger logger = Logger.getLogger(MultipleCommandExecutor.class);
	private Integer SESSION_POOL_SIZE = 4; //线程池数量。也决定了同时执行的任务数量
	public static final String OUTER_SEPERATOR = "###!##!#!##!###";
	public static final String INNER_SEPERATOR = "@@@&@@&@&@@&@@@";
	private MultipleCommand command;
	private Connection conn;
	public MultipleCommandExecutor(UserInfo userInfo, MultipleCommand command) throws IOException {
		this.conn = userInfo.login();
		this.command = command;
	}

	@Override
	public MultipleResult execute() {
		return execByMultipleSession();
//		return execBySingleSession();
	}

	/**
	 * 一次连接，开启一个session执行所有的脚本
	 * @return
	 */
	private MultipleResult execBySingleSession(){
		JSONArray commandArray = command.getCommandArray();
		Session session = null;
		try {
			session = conn.openSession();
			session.requestPTY("dumb", 90, 30, 0, 0, null);
			session.startShell();
		} catch (IOException e1) {
			e1.printStackTrace();
			return null;
		}
		OutputStream os = null;
		os = session.getStdin();
		StdoutThread successInfoThread = new StdoutThread(session);
		Thread successThread = new Thread(successInfoThread);
		successThread.start();
		long now = System.currentTimeMillis();
		int size = 0;
		if(commandArray != null && commandArray.size() != 0){
			size = commandArray.size();
			for(int i = 0; i < commandArray.size(); i++){
				JSONObject command =  commandArray.getJSONObject(i);
				logger.info("正在执行 " + command.getString("compliantnumber"));
				String filename = ObjectUtil.filenameGenerator("sh");
				try{
					//创建脚本
					createShellScriptBySFTPv3(filename, getCombinateScript(command.getString("script"), command.getString("compliantnumber")), conn);
					
					//修改权限，执行脚本
					os.write(chmod(filename).getBytes());os.write(KeyEvent.VK_ENTER);
					os.write(execSh(filename).getBytes());os.write(KeyEvent.VK_ENTER);
					
				} catch (Exception e){
					e.printStackTrace();
				} finally {
					delShellScriptBySFTPv3(filename, conn);
				}

			}
		}
		eofFlag(os);
		try {
			successThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			try {
				os.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		logger.info("本次指令执行完毕，执行" + size + "个脚本，共耗时 " + (System.currentTimeMillis() - now) + "ms");	
		Result successReslut = successInfoThread.getResult();
		return new MultipleResultHandler().handler(successReslut);
	}
	
	private static String getSuffix(String compliantNo){
		return  "\necho \"" + INNER_SEPERATOR + compliantNo + OUTER_SEPERATOR + "\"";
	}
	
	/**
	 * 给原有脚本中第一行添加个suffix，方便区分拆分
	 * @param script
	 * @return
	 */
	private static String getCombinateScript(String script, String compliantNo){
		return script + getSuffix(compliantNo);
	}
	
	/**
	 * 一次连接登录，开启多个session执行脚本，如需多线程执行，可在此方法上改造。
	 * 
	 * @return
	 */
	private MultipleResult execByMultipleSession() {
		JSONArray commandArray = command.getCommandArray();
		JSONArray resultArray = new JSONArray();
		int size = 0;
		if (commandArray != null && commandArray.size() != 0) {
			size = commandArray.size();
			ConcurrentLinkedQueue<JSONObject> scriptQueue = new ConcurrentLinkedQueue<JSONObject>();
			for(int i = 0; i < commandArray.size(); i++){
				scriptQueue.add(commandArray.getJSONObject(i));
			}
			
			ConcurrentLinkedQueue<JSONObject> resultQueue = new ConcurrentLinkedQueue<JSONObject>();
			ExecutorService es = Executors.newCachedThreadPool();
			SessionPool sp = new SessionPool(SESSION_POOL_SIZE);
			try{
				for(int i = 0; i < size; i++){
					es.execute(new ExecutorThread(scriptQueue, resultQueue, sp));
				}
				
				es.shutdown();
				
				while(true){
					if(es.isTerminated())
						break;
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						logger.error("thread error!", e);
						e.printStackTrace();
					}
				}
			} finally {
				if(sp != null)
					sp.destory();
			}
			resultArray = JSONArray.fromObject(resultQueue);
		}
		
		MultipleResult r = new MultipleResult(resultArray);
		
		return r;
	}
	
	class ExecutorThread implements Runnable{
		ConcurrentLinkedQueue<JSONObject> scriptQueue;
		ConcurrentLinkedQueue<JSONObject> resultQueue;
		SessionPool sp;
		
		public ExecutorThread(ConcurrentLinkedQueue<JSONObject> scriptQueue, ConcurrentLinkedQueue<JSONObject> resultQueue, SessionPool sp){
			this.scriptQueue = scriptQueue;
			this.resultQueue = resultQueue;
			this.sp = sp;
		}
		
		@Override
		public void run() {
			JSONObject command = scriptQueue.poll();
//			logger.info("准备执行 " + command.getString("compliantnumber"));
			
			Session session = null;
			try {
				session = sp.get();
				if(session == null){
					logger.error("执行 " + command.getString("compliantnumber") + " 发生错误! can't get session!");
					return;
				}
				logger.info("正在执行 " + command.getString("compliantnumber"));
				Result info = multipleExe(session, rebuiltScript(command.getString("script")));
				info = new TrimResultForAIX().handler(info);
				JSONObject resultObj = new JSONObject();
				resultObj.element(MultipleResult.RESULT, info.getMessage());
				resultObj.element(MultipleResult.COMPLIANTNUMBER, command.getString("compliantnumber"));
				resultObj.element(MultipleResult.CODE, info.getCode());
				resultQueue.add(resultObj);
			} finally {
				sp.put(session);
//				logger.info("执行完毕 " + command.getString("compliantnumber") + " 耗时: " + (System.currentTimeMillis() - start) + " ms");
			}
		}
		
	}
	
	class SessionPool{
		private BlockingQueue<Session> sessionQueue;
		
		public SessionPool(int poolSize){
			if(poolSize <= 0)
				throw new IllegalArgumentException("pool size should >= 0 ");
			
			sessionQueue = new ArrayBlockingQueue<Session>(poolSize);
			
			for(int i = 0; i < poolSize; i++){
				try {
					Session session = conn.openSession();
					
					sessionQueue.add(session);
					
					session.requestPTY("dumb", 90, 30, 0, 0, null);
					session.startShell();
					//等待session连接。否则下面的修改权限等命令可能会空跑，导致无法退出流
					session.waitForCondition(ChannelCondition.STDOUT_DATA, Long.MAX_VALUE);
				} catch (IOException e) {
					logger.error(e);
					e.printStackTrace();
				}
			}
		}
		
		public Session get(){
			try {
				return sessionQueue.take();
			} catch (InterruptedException e) {
				return null;
			}
		}
		
		public void put(Session session){
			sessionQueue.offer(session);
		}
		
		public void destory(){
			for(Session s : sessionQueue)
				s.close();
		}
	}
	
	/**
	 * 重新组合脚本，每个脚本后面加echo‘here is the end___’
	 * @return
	 */
	private String rebuiltScript(String command){
		return command + "\necho '" + EOF + "'";
	}
	private Result multipleExe(Session session, String command) {
		session.waitForCondition(ChannelCondition.EOF, 200);
		String filename = ObjectUtil.filenameGenerator("sh");
		try {
			OutputStream os = session.getStdin();
			//创建脚本
			createShellScriptBySFTPv3(filename, command, conn);
			
			StdoutThread successInfoThread = new StdoutThread(session);
			Thread successThread = new Thread(successInfoThread);

			//修改权限，执行脚本
			os.write(chmod(filename).getBytes());os.write(KeyEvent.VK_ENTER);
			os.write(execSh(filename).getBytes());os.write(KeyEvent.VK_ENTER);
				
			successThread.start();
			
			successThread.join();
			// 返回结果
			Result sResult = successInfoThread.getResult();
			
			return sResult;
		} catch (IOException e) {
			return new ResultInfo(false, 128, e.getMessage());
		} catch (InterruptedException e) {
			return new ResultInfo(false, 129, ErrorMessageUtil.getErrorMessageByErrorCode(129));
		} finally {
			delShellScriptBySFTPv3(filename, conn);
		}
	}
	
	
}
